package com.example.vo;

import com.example.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import java.util.*;

public class QuerySpecification {

    private static final String AND = "and";
    private static final String OR = "or";

    public static <T> Specification<T> buildQueryParam(final List<QueryParam> queryParams, final String connector) {
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                if (null != queryParams && queryParams.size() > 0) {

                    List<Predicate> predicates = new ArrayList<>();
                    for (QueryParam filter : queryParams) {
                        String[] names = StringUtil.split(filter.sortName, ".");
                        Path expression = root.get(names[0]);
                        for (int i = 1; i < names.length; i++) {
                            expression = expression.get(names[i]);
                        }

                        if (!isNullOrEmpty(filter.value)) {
                            switch (filter.operator) {
                                case EQ:
                                    predicates.add(criteriaBuilder.equal(expression, filter.value));
                                    break;
                                case NOEQ:
                                    predicates.add(criteriaBuilder.notEqual(expression, filter.value));
                                    break;
                                case LIKE:
                                    predicates.add(criteriaBuilder.like(expression, "%" + filter.value + "%"));
                                    break;
                                case NOTLIKE:
                                    predicates.add(criteriaBuilder.notLike(expression, "%" + filter.value + "%"));
                                    break;
                                case GT:
                                    predicates.add(criteriaBuilder.greaterThan(expression, (Comparable) filter.value));
                                    break;
                                case LT:
                                    predicates.add(criteriaBuilder.lessThan(expression, (Comparable) filter.value));
                                    break;
                                case GTE:
                                    predicates.add(criteriaBuilder.greaterThanOrEqualTo(expression, (Comparable) filter.value));
                                    break;
                                case LTE:
                                    predicates.add(criteriaBuilder.lessThanOrEqualTo(expression, (Comparable) filter.value));
                                    break;
                                case BETWEEN:
                                    if (root.get(filter.getSortName()).getJavaType() == Date.class) {
                                        predicates.add(criteriaBuilder.between(expression, (Date) filter.getValue(), (Date) filter.getEndValue()));
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                    }

                    // 将所有条件用 and 联合起来
                    if (!predicates.isEmpty()) {
                        if (StringUtils.isNotBlank(connector)) {
                            if (AND.equals(connector)) {
                                return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
                            } else if (OR.equals(connector)) {
                                return criteriaBuilder.or(predicates.toArray(new Predicate[predicates.size()]));
                            }
                        }
                    }
                }
                return criteriaBuilder.conjunction();
            }
        };
    }

    public static <T> Specification<T> buildQueryParam(final List<QueryParam> queryParams) {
        return buildQueryParam(queryParams, AND);
    }

    private static boolean isNullOrEmpty(Object obj) {

        if (obj == null) {
            return true;
        }
        if (obj.equals("null")) {
            return true;
        }
        if (obj instanceof CharSequence) {
            return ((CharSequence) obj).length() == 0;
        }
        if (obj instanceof Collection) {
            return ((Collection) obj).isEmpty();
        }
        if (obj instanceof Map) {
            return ((Map) obj).isEmpty();
        }
        if (obj instanceof Object[]) {
            Object[] object = (Object[]) obj;
            if (object.length == 0) {
                return true;
            }
            boolean empty = true;
            for (int i = 0; i < object.length; i++) {
                if (!isNullOrEmpty(object[i])) {
                    empty = false;
                    break;
                }
            }
            return empty;
        }
        return false;
    }
}
